package LRES;
import java.lang.Math;
import java.util.*;
/**
 * 说明:未经改动的，最原始的算法
 * 此算法是基于BP型反馈 神经网络
 * 10个输入神经元，5个隐藏神经元 ，3个输出神经元
 * teachers[][]数组为 教师数组  是输出的期望值
 * inputsarr[]数值为输入值， 为测试方便， 先把要输入的放在数组里面
 */

public class BPANN
{
	static int IN_MUN=10;                                            //输入层神经元数目
	static int HideN=5;                                              //隐层神经元数目，前馈网络隐藏层
	static int OutN=3;                                               //输出层神经元数模，各个命令的开关阀
	static int N=5;
	static double Weight_In_Hide[][] = new double[HideN][IN_MUN];    //输入层至隐层权值，输入层或者隐层的行列位置无所谓。
	static double V_Hide_Out[][] = new double[OutN][HideN];          //隐层至输出层权值，这个矩阵的行列顺序需要和上面的对应
	static double YU_HN[] = new double[HideN];                       //隐层的阈值
	static double YU_ON[] = new double[OutN];                        //输出层的阈值
	static double X[] = new double[HideN];                           //隐层的输入,各个隐藏层的神经单元的内积
	static double Y[] = new double[OutN];                            //输出层的输入，各个输出层的神经单元的内积 
	static double H[] = new double[HideN];                           //隐层的输出，s型函数之后的输出值
	static double O[] = new double[OutN];                            //输出层的输出，s型函数之后的输出值
	static double alpha;                                             //动量因子，改进型bp算法使用 
	static double err_m[] = new double[N];                          //第m个样本的总误差（误差之和）
	static double a;                                                 //输出层至隐层学习效率
	static double b;                                                 //隐层至输入层学习效率
	static double d_err[] = new double[OutN];                        //δk 隐藏层到输出层的误差
	static double e_err[] = new double[HideN];                       //δj 输入层到隐藏层的误差


	static double old_W[][] = new double[HideN][IN_MUN];             //新旧权值更新量交替--改进型Bp算法       
	static double old_V[][] = new double[OutN][HideN];

	static double teach[] = new double[OutN];                        //教师样本
	static double input[] = new double[IN_MUN];                      //学生样本

	static double teachers[][] = {{0.1f,0.1f,0.1f},{0.1f,0.1f,0.9f},{0.1f,0.9f,0.1f},{0.1f,0.9f,0.9f},{0.9f,0.1f,0.1f},{0.9f,0.1f,0.9f},{0.9f,0.9f,0.1f},{0.9f,0.9f,0.9f}};//输出期望值，一共有8组
	static double inputsarr[]={1f,0f,0f,1f,1f,0f,0f,1f,1f,0f};                   //学生标本


	/**
	 * 获取教师样本方法
	 * 选取第tm组期望值
	 */
	public boolean get_teach(int tm)
	{
		if(tm>0&&tm<9)
		{
			for(int i=0;i<OutN;i++)
				teach[i]=teachers[tm][i];
			return true;
		}
		else
			return false; 
	}

	/**
	 * 学生样本初始化
	 */
	public boolean get_pup()
	{                    
		for(int i=0;i<IN_MUN;i++)
			input[i]=inputsarr[i];

		return true;
	}



	/**
	 * 初始化权值、阈值
	 */
	public boolean initial()
	{
		/*初始化输入层到隐层的权值，随机模拟0.5~-0.5 */
		for(int i=0;i<HideN;i++)
		{
			for(int q=0;q<IN_MUN;q++)
			{      

				Weight_In_Hide[i][q]=(double)(Math.random()-0.5);

			}
		}

		/*初始化隐层到输出层的权值，随机模拟0.5~-0.5*/
		for(int i=0;i<OutN;i++)
		{
			for(int j=0;j<HideN;j++)
			{

				V_Hide_Out[i][j]= (double)(Math.random()-0.5);
			}
		}

		/*隐层阈值初始化 */
		for(int i=0;i<HideN;i++)
		{

			YU_HN[i] = (double)(Math.random()-0.5);

		}

		/*输出层阈值初始化 */
		for(int i=0;i<OutN;i++)
		{ 

			YU_ON[i] = (double)(Math.random()-0.5);
		}

		return true; 
	}



	/**
	 * 求净输入，输出
	 */
	public boolean IN_OUT()
	{
		double sigma1,sigma2;
		
		/*求隐层内积*/
		for(int i=0;i<HideN;i++)
		{
			sigma1=0.0f;
			
			for(int j=0;j<IN_MUN;j++)
				sigma1=sigma1+Weight_In_Hide[i][j]*input[j]; //对于第i个隐层单元，sigma1=第i层所有{输入值*对应的权值}的和

			X[i]=sigma1+YU_HN[i];                    //权值*输入值 总和 再 加上阈值
			H[i]=(1.0/(1.0+Math.exp(-X[i])));       //隐层内积的s型函数输出 Math.exp(double x)
		}

		/*求输出层内积*/
		for(int i=0;i<OutN;i++)
		{
			sigma2=0.0f;
			for(int j=0;j<HideN;j++)                       
				sigma2=sigma2+V_Hide_Out[i][j]*H[j];

			Y[i]=sigma2+YU_ON[i];
			O[i]=(1.0/(1.0+Math.exp(-Y[i])));   /*输出层内积的s型函数输出 Math.exp(double x)*/
		}

		return true;
	}




	/**
	 * 误差分析
	 * 隐藏层到输出层的误差
	 * δk
	 */
	public boolean Err_O_H(int ml)
	{
		double abs_err[]=new double[OutN];
		double sqr_err=0.0f;
		for (int i=0;i<OutN;i++)
		{
			abs_err[i]=teach[i]-O[i];               //误差信号 e(n)=d(n)-y(n)
			sqr_err=sqr_err+(abs_err[i])*(abs_err[i]);
			d_err[i]=abs_err[i]*O[i]*(1.0-O[i]);   //教师值与输出值的差 乘以 输出值 乘以 (1-输出值)
		}

		err_m[ml]=sqr_err/2;                            //输出层的每个输出神经元的 教师值与输出值的差 的和  除以2的值

		return true;        
	}


	/**
	 * 输入层到隐藏层的误差
	 * δj
	 */
	public boolean Err_H_I()
	{
		double sigma;
		for(int i=0;i<HideN;i++)
		{
			sigma=0.0f;
			for(int j=0;j<OutN;j++)
				sigma=sigma+d_err[j]*V_Hide_Out[j][i];

			e_err[i]=sigma*H[i]*(1-H[i]);
		}
		return true;
	}




	/**
	 * 总误差
	 * 各样本{输出层总误差}之和
	 * 当需要批量训练的时候才用
	 */
	public double Err_Sum()
	{
		double total_err=0.0f;
		
		for(int i=0;i<N;i++)
			total_err=total_err+err_m[i];

		return total_err;
	}


	/**
	 * 新旧权值更新量交替--改进型Bp算法
	 */     
	public boolean saveWV()
	{
		//输入层到隐层的权值更新
		for(int i=0;i<HideN;i++)
		{
			for(int j=0;j<IN_MUN;j++)
				old_W[i][j] =b*e_err[i]*input[j];
		}
		// 隐层到输出层的权值更新
		for(int i=0;i<OutN;i++)
		{
			for(int j=0;j<HideN;j++)
				old_V[i][j] =a*d_err[i]*H[j];
		}
		return true;
	}  



	/**
	 * 更新权值，阈值
	 * 输出层更新
	 */
	public boolean Updata_O_H()
	{
		for(int i=0;i<OutN;i++)
		{
			for (int j=0;j<HideN;j++)
				//V_Hide_Out[i][j]=V_Hide_Out[i][j]+a*d_err[i]*H[j];         
				// V_Hide_Out[i][j]=V_Hide_Out[i][j]+(1.0-alpha)*a*d_err[i]*H[j]+alpha*a*d_err[i]*H[j];
				V_Hide_Out[i][j] = V_Hide_Out[i][j]+(1.0-alpha)*a*d_err[i]*H[j]+alpha*old_V[i][j];
			YU_ON[i]=YU_ON[i]+a*d_err[i];
		}
		return true;
	}


	/**
	 * 更新权值，阈值
	 * 隐层更新
	 */
	public boolean Updata_H_I()
	{

		for (int i=0;i<HideN;i++)
		{
			for (int j=0;j<IN_MUN;j++)
				// Weight_In_Hide[j][i]=Weight_In_Hide[j][i]+b*e_err[j]*input[i];
				// Weight_In_Hide[j][i]=Weight_In_Hide[j][i]+(1.0-alpha)*b*e_err[j]*input[i]+alpha*b*e_err[j]*input[i];
				Weight_In_Hide[i][j] = Weight_In_Hide[i][j]+(1.0-alpha)*b*e_err[i]*input[j]+alpha*old_W[i][j];
			YU_HN[i]=YU_HN[i]+b*e_err[i];
		}

		return true;
	}

	/**
	 * 打印计算结果
	 */
	public void Print_Result()
	{

		System.out.println("输入层到隐藏层权值：");
		for(int i=0;i<HideN;i++)
			for(int j=0;j<IN_MUN;j++)
				System.out.println("Weight_In_Hide["+i+"]"+"["+j+"]"+"="+Weight_In_Hide[i][j]);

		System.out.println("");
		System.out.println("隐藏层到输出层权值：");
		for(int i=0;i<OutN;i++)
			for(int j=0;j<HideN;j++)
				System.out.println("V_Hide_Out["+i+"]"+"["+j+"]"+"="+V_Hide_Out[i][j]);

		System.out.println("");
		System.out.println("隐藏层阈值：");
		for(int i=0;i<HideN;i++)
			System.out.println("YU_HN["+i+"]="+YU_HN[i]);

		System.out.println("");
		System.out.println("输出层阈值：");
		for(int i=0;i<OutN;i++)
			System.out.println("YU_ON["+i+"]="+YU_ON[i]);


		System.out.println("");
		System.out.println("输出层输出值：");
		for(int i=0;i<OutN;i++)
			System.out.println("O["+i+"]="+O[i]);

	}



	public static void main(String[] args)
	{
		alpha=0.2f;                     //动量因子    
		double a=0.5f;                  //  0~1之间 ，趋于0就比较保守，趋于1就比较激进
		double b=0.5f;                  //  0~1之间 ，趋于0就比较保守，趋于1就比较激进
		int flag=200000;                //学习的极限次数
		int study=0;                    //学习次数
		double Pre_error=0.00001f;      //可接受误差
		double sum_err;                 //总误差

		BPANN newsjwl = new BPANN();
		newsjwl.get_teach(1);           //选择老师
		newsjwl.get_pup();              //学生标本
		newsjwl.initial();              //网络元初始化 



		do
		{
			if(study>=flag)
			{
				break;
			}
			++study;               
			newsjwl.IN_OUT();       //计算权值阈值
			newsjwl.Err_O_H(0);     //隐藏层到输出层误差
			newsjwl.Err_H_I();      //输入层到隐藏层误差
			newsjwl.saveWV();       //新旧权值更新量
			newsjwl.Updata_O_H();   //更新隐藏层到输出层的权值阈值          
			newsjwl.Updata_H_I();   //更新输入层到隐藏层的权值阈值
			sum_err=newsjwl.Err_Sum();    //计算总误差

		}while(sum_err>Pre_error);       //可接受误差和真实总误差的比较

		newsjwl.Print_Result();         //输出权值阈值
		
		System.out.println("学习次数："+study);


		/*

         System.out.println("");
               System.out.println("学习次数："+study);
               System.out.println(sum_err);
               System.out.println("e_err[i]:");
               for(int i=0;i<HideN;i++)
               System.out.println(e_err[i]);
               System.out.println("d_err[i]:");
               for(int i=0;i<OutN;i++)
               System.out.println(d_err[i]);
               System.out.println(""); 
		 */   
	}


}